/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-11-08     balanceTWK   first version
 */

#include <board.h>
#include "drv_soft_i2c.h"
#include "drv_config.h"

#ifdef RT_USING_I2C

//#define DRV_DEBUG
#define LOG_TAG              "drv.i2c"
#include <drv_log.h>

#if !defined(BSP_USING_I2C1) && !defined(BSP_USING_I2C2) && !defined(BSP_USING_I2C3) && !defined(BSP_USING_I2C4)
    #error "Please define at least one BSP_USING_I2Cx"
    /* this driver can be disabled at menuconfig → RT-Thread Components → Device Drivers */
#endif


/**
 * The time delay function.
 *
 * @param microseconds.
 */
static void stm32_udelay(rt_uint32_t us)
{
    rt_uint32_t ticks;
    rt_uint32_t told, tnow, tcnt = 0;
    rt_uint32_t reload = SysTick->LOAD;

    ticks = us * reload / (1000000 / RT_TICK_PER_SECOND);
    told = SysTick->VAL;
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
            {
                tcnt += told - tnow;
            }
            else
            {
                tcnt += reload - tnow + told;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                break;
            }
        }
    }
}

/*user can change this*/
#define I2CBUS_NAME "i2c1"
#define I2C_TIMEOUT 100

struct stm32_i2c_bus
{
    struct rt_i2c_bus_device parent;
    I2C_HandleTypeDef I2C_Handle;
    struct rt_i2c_msg *msg;
    rt_uint32_t msg_cnt;
    volatile rt_uint32_t msg_ptr;
    volatile rt_uint32_t dptr;
    char *device_name;
};

static struct stm32_i2c_bus stm32_i2c1 =
{
    .device_name = I2CBUS_NAME,
};

static rt_size_t stm32_i2c_mst_xfer(struct rt_i2c_bus_device *bus,
                                      struct rt_i2c_msg msgs[],
                                      rt_uint32_t num);
static rt_size_t stm32_i2c_slv_xfer(struct rt_i2c_bus_device *bus,
                                      struct rt_i2c_msg msgs[],
                                      rt_uint32_t num);
static rt_err_t stm32_i2c_bus_control(struct rt_i2c_bus_device *bus,
                                        rt_uint32_t,
                                        rt_uint32_t);

static const struct rt_i2c_bus_device_ops stm32_i2c_ops =
{
    stm32_i2c_mst_xfer,
    stm32_i2c_slv_xfer,
    stm32_i2c_bus_control,
};


static rt_err_t stm32_i2c_configure(struct stm32_i2c_bus *bus)
{
    RT_ASSERT(bus != RT_NULL);

    bus->parent.ops = &stm32_i2c_ops;
    HAL_I2C_MspInit(&bus->I2C_Handle);

    if (HAL_I2C_Init(&bus->I2C_Handle) != HAL_OK)
    {
        LOG_E("HAL_I2C_Init fail \n");
        return -RT_ERROR;
    }

    /**Configure Analogue filter */
    if (HAL_I2CEx_ConfigAnalogFilter(&bus->I2C_Handle, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
    {
        LOG_E("HAL_I2CEx_ConfigAnalogFilter fail \n");
        return -RT_ERROR;
    }

    /**Configure Digital filter */
    if (HAL_I2CEx_ConfigDigitalFilter(&bus->I2C_Handle, 0) != HAL_OK)
    {
        LOG_E("HAL_I2CEx_ConfigDigitalFilter fail \n");
        return -RT_ERROR;
    }

    /* HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C1); */
    return RT_EOK;
}

static rt_size_t stm32_i2c_mst_xfer(struct rt_i2c_bus_device *bus,
                                      struct rt_i2c_msg msgs[],
                                      rt_uint32_t num)
{
    struct stm32_i2c_bus *stm32_i2c;

    RT_ASSERT(bus != RT_NULL);
    stm32_i2c = (struct stm32_i2c_bus *) bus;

    stm32_i2c->msg = msgs;
    stm32_i2c->msg_ptr = 0;
    stm32_i2c->msg_cnt = num;
    stm32_i2c->dptr = 0;

    if (num == 1)
    {
        if (stm32_i2c->msg[0].flags & RT_I2C_RD)
        {
            if (HAL_I2C_Master_Receive(&stm32_i2c->I2C_Handle, stm32_i2c->msg[0].addr << 1, stm32_i2c->msg[0].buf, stm32_i2c->msg[0].len, I2C_TIMEOUT) != HAL_OK)
            {
                return 0;
            }
        }
        else
        {
            if (HAL_I2C_Master_Transmit(&stm32_i2c->I2C_Handle, stm32_i2c->msg[0].addr << 1, stm32_i2c->msg[0].buf, stm32_i2c->msg[0].len, I2C_TIMEOUT) != HAL_OK)
            {
                return 0;
            }
        }
    }
    else
    {
        if (stm32_i2c->msg[1].flags & RT_I2C_RD)
        {
            if (HAL_I2C_Mem_Read(&stm32_i2c->I2C_Handle, stm32_i2c->msg[0].addr << 1, *stm32_i2c->msg[0].buf, stm32_i2c->msg[0].len, stm32_i2c->msg[1].buf, stm32_i2c->msg[1].len, I2C_TIMEOUT) != HAL_OK)
            {
                return 0;
            }
        }
        else
        {
            if (HAL_I2C_Mem_Write(&stm32_i2c->I2C_Handle, stm32_i2c->msg[0].addr << 1, *stm32_i2c->msg[0].buf, stm32_i2c->msg[0].len, stm32_i2c->msg[1].buf, stm32_i2c->msg[1].len, I2C_TIMEOUT) != HAL_OK)
            {
                return 0;
            }
        }
    }

    stm32_i2c->msg = RT_NULL;
    stm32_i2c->msg_ptr = 0;
    stm32_i2c->msg_cnt = 0;
    stm32_i2c->dptr = 0;
    return num;
}

static rt_size_t stm32_i2c_slv_xfer(struct rt_i2c_bus_device *bus,
                                      struct rt_i2c_msg msgs[],
                                      rt_uint32_t num)
{
    return 0;
}

static rt_err_t stm32_i2c_bus_control(struct rt_i2c_bus_device *bus,
                                        rt_uint32_t cmd,
                                        rt_uint32_t arg)
{
    return RT_ERROR;
}

/* if i2c is locked, the function will unlock it  */
static rt_err_t stm32_i2c_bus_unlock_bypass(void)
{
    rt_size_t t = 0;
    GPIO_InitTypeDef GPIO_InitStruct;
    rt_err_t result;

    /* config pin0 and pin1 as simulator i2c */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);//initial high

    if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9))
    {
        while (t++ < 9)
        {
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
            stm32_udelay(10);
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
            stm32_udelay(10);
        }
    }

    if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9))
    {
        result = -RT_ERROR;
    }
    else
    {
        result = RT_EOK;
    }

    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8);
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_9);
    stm32_udelay(100);

    return result;
}

int rt_hw_i2c_init_bypass(void)
{
    #if 0
    if (stm32_i2c_bus_unlock_bypass() != RT_EOK)
    {
        LOG_E("Failed to unlock i2c.");
        return 0;
    }
    #endif
    stm32_i2c1.I2C_Handle.Instance = I2C1;
    /* stm32_i2c1.I2C_Handle.Init.Timing = 0x0050174F;//1000k */
    stm32_i2c1.I2C_Handle.Init.OwnAddress1 = 0;
    stm32_i2c1.I2C_Handle.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    stm32_i2c1.I2C_Handle.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    stm32_i2c1.I2C_Handle.Init.OwnAddress2 = 0;
    stm32_i2c1.I2C_Handle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    stm32_i2c1.I2C_Handle.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    if (stm32_i2c_configure(&stm32_i2c1) == RT_EOK)
    {
        rt_i2c_bus_device_register(&stm32_i2c1.parent, stm32_i2c1.device_name);
    }
    else
    {
        LOG_E("stm32_i2c_configure fail");
    }
    return 0;
}
INIT_BOARD_EXPORT(rt_hw_i2c_init_bypass);
#endif /* RT_USING_I2C */
